<?php

namespace App\Http\Controllers\Api;

use App\Exceptions\ResponseException;
use App\Models\Goods;
use App\Models\GoodsSku;
use App\Models\GoodsSpec;
use App\Models\Order;
use App\Models\OrderInfo;
use App\Models\SeckillGoods;
use App\Models\SpecValue;
use App\Task\DecStockTask;
use Hhxsv5\LaravelS\Swoole\Task\Task;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
use Whoops\Exception\ErrorException;

/**
 * 秒杀控制器，查询秒杀商品和库存
 * @package App\Http\Controllers\Api
 */
class SecKillController extends Controller
{

    /**
     * 秒杀商品列表
     * User: Clannad ~ ☆
     * Date: 2020/3/11 0011 10:19
     * @throws ResponseException
     */
    public function index()
    {
        $seckillGoods = SeckillGoods::query()
            ->select('id','goods_id','goods_name','thumb','price','seckill_price','start_time','end_time','inventory','sale_num')
            ->orderBy('sort','desc')
            ->where('kill_status',1)
            ->where('end_time','>',date('Y-m-d H:i:s',time()))
            ->get()->toArray();

        throw new ResponseException(200,'ok',$seckillGoods);
    }

    /**
     * 秒杀商品显示
     * User: Clannad ~ ☆
     * Date: 2020/3/11 0011 12:24
     * @throws ResponseException
     */
    public function goods(Request $request,$id)
    {
        //查询是否存在
        $seckill = SeckillGoods::query()
            ->select('id','goods_id','goods_name','thumb','price','seckill_price','kill_status','single_num','start_time','end_time','inventory','sale_num','sort')
            ->where(['kill_status'=>1])
            ->where('id',$id)->first();

        if(!$seckill){
            throw new ResponseException(404,'秒杀商品不存在或活动已过期');
        }
        //查询秒杀的时间 库存量 状态 是否合法
        $cur_time = date('Y-m-d H:i:s',time());
        if($seckill->kill_status != 1 || $seckill->start_time > $cur_time || $seckill->end_time < $cur_time)
        {
            throw new ResponseException(400,'请求不合法');
        }

        $goods_id = $seckill->goods_id;

        //商品基本信息
    	$goods = Goods::query()
            ->select('id','goods_name','thumb','price','description')
            ->where([
                'id'=>$goods_id,
                'status'=>1
            ])
            ->first();

        if(!$goods){
            throw new ResponseException(404,'商品不存在');
        }

        //整理商品信息字段
        $goods['goods_name'] = $seckill->goods_name;

        //前台展示的规格信息
		$goodsSpec = GoodsSpec::query()
            ->select('goods_id','spec_id','spec.spec_name',\DB::raw('GROUP_CONCAT(spec_value_id) as spec_value_id'))
            ->leftJoin('spec','goods_spec.spec_id','=','spec.id')
            ->where('goods_id',$goods_id)
            ->groupBy(['goods_spec.spec_id','goods_id','spec_id','spec.spec_name'])
            ->get()->toArray();

        foreach ($goodsSpec as $k => $v) {
            $spec_item = SpecValue::query()
                ->select('id','value')
                ->whereIn('id',explode(',',$v['spec_value_id']))
                ->get()->toArray();

            $goodsSpec[$k]['spec_items'] = $spec_item;
        }

		//商品相册
		$goods_album = $goods->goods_album()->select('image')->get()->toArray();

        //商品的库存信息
        $goods_inventory = GoodsSku::query()
            ->select(['sku_list','inventory','price'])
            ->where([
                'goods_id'=>$goods_id,
            ])->get()->toArray();

		$data = [
            'goods'=>$goods,
            'goods_spec'=>$goodsSpec,
            'goods_album'=>$goods_album,
            'seckill'=>$seckill->toArray(),
            'goods_inventory'=>$goods_inventory
        ];

		throw new ResponseException(200,'ok',$data);
    }

    /**
     * 获得库存
     * User: Clannad ~ ☆
     * Date: 2020/3/11 0011 21:28
     */
    public function getInventory()
    {
        $id = request()->input('id');
        $sku = request()->input('sku_list');
        $active_id = request()->input('active_id');

//        $key = "seckill_goods_stock:goods_id:$id:sku:$sku";
//        $len = Redis::llen($key);

        $key = $key = 'goods_sku_stock:goods_id:'.$id.':active_id:'.$active_id;
        $len = Redis::hget($key,$sku);

        //记录日志
        $data = ['code' => 200,'msg'=>'ok','data'=>['inventory'=>$len,'key'=>$key]];
        Log::info('接收的id---' . $id . '----' . $sku . '---' . json_encode($data));

        return response()->json(['code' => 200,'msg'=>'ok','data'=>['inventory'=>$len]]);
    }

    /**
     * 秒杀商品到购物车
     * User: Clannad ~ ☆
     * @return \Illuminate\Http\JsonResponse
     */
    public function killGoodsToConfirm(Request $request)
    {
        $goods_id = $request->input('goods_id');
        $sku_list = $request->input('sku_list');
//        $goods_num = $request->input('goods_num');
        $active_id = $request->input('active_id'); //活动id

        try{
            //查该场活动是否存在
            $seckill = SeckillGoods::query()->where([
                'id'=>$active_id,
                'kill_status'=>1,
                ['start_time','<',date('Y-m-d H:i:s',time())],
                ['end_time','>',date('Y-m-d H:i:s',time())]
            ])->first();

            if(!$seckill){
                return  response()->json(['code'=>400,'msg'=>'该场活动已结束']);
            }
            //先判断用户是否已经够买了 如果抢到了 没有下单 会提示用户去下单
            $user_id = auth('api')->id();

            //预下单的key
            $preparation = Redis::keys('preparation:user_id:'.$user_id.':goods_id:'.$goods_id.'*');

            if($preparation){
                return response()->json(['code'=>4001,'msg'=>'您已经抢过了，15分钟有效，请尽快完成订单确认']);
            }

            // 已经下单的key 不管什么配置规格 只要下单了，就算已经买过了
            $createOrder = Redis::keys('create_sckill_order:'.$goods_id.':user_id:'.$user_id.'*');

            if($createOrder){
                return response()->json(['code'=>400,'msg'=>'您已经下单了，请先付款']);
            }

            //已经有支付的订单
            $alreadyShopping = Redis::get('already_shopping:'.$goods_id.':user_id:'.auth('api')->id());

            if($alreadyShopping){
                return response()->json(['code'=>400,'msg'=>'您已经抢购过了哦~']);
            }

            //验证库存
            $goods_sku_number_key = $key = 'goods_sku_stock:goods_id:'.$goods_id.':active_id:'.$active_id;
            $goods_sku_number = Redis::hget($goods_sku_number_key,$sku_list);

            if((int)$goods_sku_number < 1){
                return response()->json(['code'=>4002,'msg'=>'商品信息已过期,正在刷新页面']);
            }

            //进行商品验证 判断下单时间 有效性等
            $goods = SeckillGoods::query()
                ->where([
                    'goods_id'=>$goods_id,
                    'id'=>$active_id,
                    'kill_status'=>1,
                    ['start_time','<',date('Y-m-d H:i:s',time())],
                    ['end_time','>',date('Y-m-d H:i:s',time())],
                ])->first();

            if(!$goods){
                return response()->json(['code'=>400,'msg'=>'商品信息不合法或商品不存在']);
            }

            /*if($goods_num != $goods['single_num']){
                return response()->json(['code'=>400,'msg'=>'购买数量超过超过限购数量']);
            }*/

            //流程  秒杀下单时 传入商品id 商品规格，用于加入订单确认页

            //相应库存出队
            $stock_key = "seckill_goods_stock:goods_id:$goods_id".':active_id:'.$active_id;
            $popGoods = Redis::rpop($stock_key);

            Log::info('秒杀出队：'.$stock_key.'-----'.$popGoods);

            if(!$popGoods){
                return response()->json(['code'=>400,'msg'=>'商品被抢光了，尚有用户未付款，15分钟后您仍有机会','data'=>[]]);
            }
            //进入相对应的商品规格减库存队列
            Redis::lpush('dec_seckill_goods_stock:goods_id:'.$goods_id.':active_id:'.$active_id,$popGoods);

            //15分钟有效 用于下单后减库存
            Redis::setex('preparation:user_id:'.$user_id.':goods_id:'.$goods_id.':sku:'.$sku_list.':active_id:'.$active_id,300,
                json_encode(['user_id'=>$user_id,'goods_id'=>$goods_id,'sku'=>$sku_list,'active_id'=>$active_id])
            );

            //操作hash库存 - 1
            Redis::hset($goods_sku_number_key,$sku_list,--$goods_sku_number);

            //返回抢购成功信息给前端
            return response()->json(['code'=>200,'msg'=>'抢购成功！5分钟有效，请尽快确认订单']);
        }catch (\Exception $e){
            Log::info('抢购失败错误：' . $e->getMessage());
            return response()->json(['code'=>500,'msg'=>'抢购失败，服务器内部错误！请稍后再试','data'=>[]]);
        }

        //Log::info('接收的id---' . $id . '----' . $sku . '---' . json_encode($data));
//        return response()->json($data);
    }


    /**
     * 获得待确认信息
     * User: Clannad ~ ☆
     * Date: 2020/3/13 0013 22:40
     */
    public function getConfirmInfo(Request $request)
    {
        $user_id = auth('api')->id();
        $goods_id = $request->input('goods_id');
        //从redis中找出预订单key
        $preparationInfo = Redis::keys('preparation:user_id:'.$user_id.':goods_id:'.$goods_id.'*');

        if(!$preparationInfo){
            return response()->json(['code'=>404,'msg'=>'订单确认信息已过期或不存在']);
        }
        //查找预确认订单数据
        $preparationInfo = Redis::get($preparationInfo['0']);
        $preparationInfo = json_decode($preparationInfo,true);

        $sku_list = $preparationInfo['sku'];

        //判断商品是否存在
        $goods = SeckillGoods::query()
            ->select('seckill_goods.goods_name','seckill_goods.thumb','seckill_goods.seckill_price as price','single_num as number')
            ->leftJoin('goods','goods.id','=','seckill_goods.goods_id')
            ->where([
                'seckill_goods.goods_id'=>$goods_id,
                'goods.status'=>1,
                'seckill_goods.kill_status'=>1,
                ['seckill_goods.start_time','<',date('Y-m-d H:i:s',time())],
                ['seckill_goods.end_time','>',date('Y-m-d H:i:s',time())],
            ])->first();

        if(!$goods){
            return response()->json(['code'=>404,'msg'=>'商品不存在']);
        }

//        $goods['number'] = 1;

        //判断商品规格
        $sku = GoodsSku::query()->where([
            'goods_id'=>$goods_id,
            'sku_list'=>$sku_list
        ])->first();

        if(!$sku){
            return response()->json(['code'=>'404','msg'=>'商品规格不存在']);
        }

//        $goods['price'] = $sku['price'];

        //获得sku_info
        $goods['sku_info'] = SpecValue::query()
            ->select('spec.spec_name','spec_value.value')
            ->leftJoin('spec','spec_value.spec_id','=','spec.id')
            ->whereIn('spec_value.id',explode(',',$sku_list))
            ->get()->toArray();

        //查询收货地址
        $user_address = auth('api')->user()->user_address()
            ->select('user_address.id', 'user_id', 'username', 'tel', 'user_address.county_id',
                \DB::raw('REPLACE(my_region.whole_name,",","") as area'),
                'address', 'default')
            ->leftJoin('region','user_address.county_id','=','region.county_id')
            ->groupBy(['user_address.id'])
            ->orderBy('default','desc')
            ->get()->toArray();

        $data = [
            'cart_list'    => [$goods],
            'user_address' => $user_address,
            'sku' => $sku_list,
            'active_id'=>$preparationInfo['active_id'],//活动的id
        ];

        return response()->json(['code'=>200,'msg'=>'ok','data'=>$data]);
    }


    /**
     * 创建订单
     * User: Clannad ~ ☆
     * @param Request $request
     */
    public function createOrder(Request $request)
    {
        $goods_id = $request->input('goods_id');
        $user_addr_id = $request->input('user_addr_id');
        $remark = $request->input('remark');
        $sku = $request->input('sku');
        $express_type = $request->input('express_type');
        $active_id = $request->input('active_id');
        $user_id = auth('api')->id();

        //判断预订单的key是否存在，不存在的话说明已经过期了
        $preparationInfo = Redis::get('preparation:user_id:'.$user_id.':goods_id:'.$goods_id.':sku:'.$sku.':active_id:'.$active_id);

        if(!$preparationInfo){
            return response()->json(['code'=>404,'msg'=>'订单确认信息已过期，或不存在']);
        }

        //判断商品是否存在
        $goods = SeckillGoods::query()
            ->select('seckill_goods.goods_name','seckill_goods.thumb',
                'seckill_goods.seckill_price as price',
                'seckill_goods.price as org_price',
                'single_num as number')
            ->leftJoin('goods','goods.id','=','seckill_goods.goods_id')
            ->where([
                'seckill_goods.goods_id'=>$goods_id,
                'goods.status'=>1,
                'seckill_goods.kill_status'=>1,
                ['seckill_goods.start_time','<',date('Y-m-d H:i:s',time())],
                ['seckill_goods.end_time','>',date('Y-m-d H:i:s',time())],
            ])->first();

        if(!$goods){
            throw new ResponseException(404,'商品不存在');
        }

        $preparationInfo = json_decode($preparationInfo,true);
        $sku_list = $preparationInfo['sku'];
        //判断库存是否足
        $sku = GoodsSku::query()->where([
            'goods_id'=>$goods_id,
            'sku_list'=>$sku_list
        ])->first();

        if(!$sku){
            throw new ResponseException(400,'商品规格不存在');
        }

        //判断用户地址
        $user_address = auth('api')->user()->user_address()->where('id',$user_addr_id)->first();

        if(!$user_address){
            throw new ResponseException(400,'用户地址不存在');
        }

        \DB::beginTransaction();

        try{
            $data = [
                'order_sn'=>'ms' . createUniqueOrderSn(),
                'order_type'=>2,//2 秒杀订单
                'user_id'=>auth('api')->id(),
                'province_id'=>$user_address['province_id'],
                'city_id'=>$user_address['city_id'],
                'county_id'=>$user_address['county_id'],
                'address'=>$user_address['address'],
                'username'=>$user_address['username'],
                'tel'=>$user_address['tel'],
                'remark'=>$remark,
                'express_type'=>$express_type,
                'express_price'=>$express_type == 1 ? 8 : 0,
                'goods_total'=>1,
                'org_price_total'=>$goods['price'] ,// 商品原价(秒杀时是秒杀价格)
                'active_id'=>$active_id ,// 活动id
            ];

            $data['price_total']=$goods['price'] + $data['express_price']; //秒杀价格 + 运费 = 总额

            $order = Order::create($data);

            if($order->id){
                $order_info = [
                    'order_id'=>$order['id'],
                    'order_sn'=>$data['order_sn'],
                    'goods_id'=>$goods_id,
                    'goods_name'=>$goods['goods_name'],
                    'thumb'=>$goods['thumb'],
                    'sku_list'=>$sku_list,
                    'number'=>1,
                    'price'=>$goods['price'],
                ];

                $orderInfoModel = OrderInfo::create($order_info);

                if($orderInfoModel->id){
                    //返回订单号给前端
                    \DB::commit();
                    //删除预订单的key
                    Redis::del('preparation:user_id:'.$user_id.':goods_id:'.$goods_id.':sku:'.$sku_list.':active_id:'.$active_id);
                    //写入个已下单的key 以便于key过期的时候删除订单 还原库存  处理用户骚操作为订单下单 但未付款
                    Redis::setex('create_sckill_order:'.$goods_id.':user_id:'.$user_id.':sku:'.$sku_list.':order_sn:'.$data['order_sn'].':active_id:'.$active_id,900,$goods_id);
                    //异步减库存
                    $taskData = [
                        'goods_id'=>$goods_id,
                        'user_id'=>$user_id,
                        'sku'=>$sku['sku_list'],
                        'order_sn'=>$data['order_sn'],
                        'active_id'=>$active_id
                    ];

                    $task = new DecStockTask($taskData);
                    $result = Task::deliver($task);

                    Log::info('秒杀减库存任务投递：'. json_encode($result));

                    return response()->json(['code'=>200,'msg'=>'下单成功','data'=>['order_sn'=>$data['order_sn']]]);
                }else{
                    \DB::rollBack();
                    return response()->json(['code'=>500,'msg'=>'下单失败，请联系管理员','data'=>[]]);
                }
            }else{
                \DB::rollBack();
                return response()->json(['code'=>500,'msg'=>'下单失败，请联系管理员','data'=>[]]);
            }
        }catch (ErrorException $e){
            \DB::rollBack();
            \Log::info('下单错误：' . $e->getMessage());
            return response()->json(['code'=>500,'msg'=>'下单失败，请联系管理员','data'=>[]]);
        }
    }
}
